use std::collections::HashMap;

const CHECK_CODE: [char; 11] = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
const WEIGHTS: [u32; 17] = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];

struct IdentityCard {
    valid: bool,
    area_code: String,
    birthday: String,
    order_code: String,
}

impl IdentityCard {
    fn new(id_card_no: &str) -> Self {
        let valid = Self::check_valid(id_card_no);
        if !valid {
            return Self {
                valid: false,
                area_code: "".to_string(),
                birthday: "".to_string(),
                order_code: "".to_string(),
            }
        }
        let area_code = id_card_no[0..6].to_string();
        let offset = if id_card_no.len() == 18 { 2 } else { 0 };
        let birthday = id_card_no[6..12+offset].to_string();
        let birthday = if offset == 2 {
            birthday
        } else {
            "19".to_string() + &birthday
        };
        let order_code = id_card_no[12+offset..15+offset].to_string();
        Self {
            valid,
            area_code,
            birthday,
            order_code,
        }
    }

    fn check_valid(id_card_no: &str) -> bool {
        if id_card_no.len() == 18 {
            for c in id_card_no.chars().take(17) {
                if !c.is_digit(10) {
                    return false;
                }
            }
            if let Some(last) = id_card_no.chars().last() {
                return last == Self::calculate_check_code(id_card_no);
            }
        } 
        if id_card_no.len() == 15 {
            for c in id_card_no.chars() {
                if !c.is_digit(10) {
                    return false;
                }
            }
            return true
        }
        false
    }

    fn calculate_check_code(id_card_no: &str) -> char {
        let mut sum = 0;
        for (i, &weight) in WEIGHTS.iter().enumerate() {
            if let Some(digit) = id_card_no.chars().nth(i).and_then(|c| c.to_digit(10)) {
                sum += digit * weight;
            }
        }
        CHECK_CODE[(sum % 11) as usize]
    }
}

fn get_location_map() -> HashMap<String, HashMap<String, String>> {
    let json_str = std::fs::read_to_string("govCode.json").unwrap();
    let data: HashMap<String, HashMap<String, String>> = serde_json::from_str(&json_str).unwrap();
    data
}


pub fn check_id_card(id_card_no: &str) -> String {
    let id_card = IdentityCard::new(id_card_no);
    if ! id_card.valid {
        return "身份证号码错误".to_string();
    }
    // 提取性别
    let gender = if id_card.order_code.parse::<u32>().unwrap() % 2 == 0 {
        "女"
    } else {
        "男"
    };
    // 提取出生日期
    let birth_date = format!("{}年{}月{}日", &id_card.birthday[0..4], &id_card.birthday[4..6], &id_card.birthday[6..8]);
    // 提取籍贯
    let location_map = get_location_map();
    
    let province_code = id_card.area_code[0..2].to_string() + "0000";
    let city_code = id_card.area_code[0..4].to_string() + "00";
    let area_code = id_card.area_code.to_string();
    let province = location_map.get(&province_code).unwrap().get("text").unwrap();
    let city = if let Some(city) = location_map.get(&city_code) {
        city.get("text").unwrap()
    } else {
        "市辖区"
    };
    let area = location_map.get(&area_code).unwrap().get("text").unwrap();
    let location = format!("{}-{}-{}", province, city, area);
    format!("身份证号码正确,{},{},{}", gender, birth_date, location)
}
